%{
	#include <string.h>
	#include "collapsej.c"


	#ifndef __INCLL
	#define __INCLL
	#pragma once
	#include "ll.c"
	#endif

	#define DEBUG 1
	
	int mode=0;
	int contador=0;
	int lines=0;
	LinkedList * qclasses_ll;
	AccessModifier lastAccessModifier;
	int opened_scopes=0;
	FILE * file;
%}

ACCESS_MODIFIER (private|protected|public)

OPEN_SCOPE (\{)

CLOSE_SCOPE \}

NEW_LINE \n

SPACE ([ ])

QCLASS	{SPACE}*(quantum){SPACE}+(class){SPACE}+([a-zA-Z,])+

QENTANGLES (entangles){SPACE}+([a-zA-Z,])+

QSTATE 	{SPACE}*(state){SPACE}+([a-zA-Z,])+{SPACE}+

QBEHAVIOR {SPACE}*(behavior){SPACE}+([a-zA-Z,])+

QSCOLLAPSE {SPACE}*([a-zA-Z,])+{SPACE}*(->){SPACE}*([a-zA-Z,])+(;)

QINVOKEBEHAVIOR {SPACE}*([a-zA-Z])+([.])([a-zA-Z])+(\(\);)


DEBUG 	(debug)



RESTO (.)

END 	\\0

%%

{DEBUG} {
	int i,j;
	Node ** classes_array;
	
	printf("\nOPENED_SCOPES=[%d]\tMODE=[%d]\n", opened_scopes,mode);
	printf("COUNT OF QCLASSES=[%d]\n",qclasses_ll -> length);
	
	//print all the quantum classes
	classes_array = getArray(qclasses_ll);
	
	for(i=0;i< qclasses_ll->length; i++){
		printf("Iteration [%d]\n",i);
		QClass * qc = classes_array[i]->value;
		
		printf("classes_array[%d]=[%s]{\n",i,qc->name);
		printf("Access Modifier: [%s]\n",qc->access_modifier.line==-1?"":qc->access_modifier.name);
		
		if(qc->code_length > 0){
			int counter = 0;
			printf("\n\tCode length=[%d] {\n\t",qc->code_length);
			for(;counter < qc->code_length;++counter)
				printf("%c",qc->code[counter]);
			printf("\n\t}\n");	
		}
		
		if (qc->qstates != NULL &&
			qc->qstates->length > 0){
			
			printf("\tThis class has %d quantum states\n",qc->qstates->length);
			//retrieves an array of quantum states for this quantum class
			Node ** states_array = qc-> qstates-> getArray(qc->qstates);
			
			//print all the quantum states
			for(j=0;j< qc->qstates->length; j++){
				QState * qs = (QState*)((Node*)(states_array[j]))->value;
				printf("\tIteration [%d]\n",j);
				printf("\tstates_array[%d]=[%s]{\n",j,qs->name);
				printf("Access Modifier: [%s]\n",qs->access_modifier.line==-1?"":qs->access_modifier.name);
				
				if(qs->code_length > 0){
					int counter = 0;
					printf("\n\t\tCode length=[%d] {\n\t\t",qs->code_length);
					for(;counter < qs->code_length;++counter)
						printf("%c",qs->code[counter]);
					printf("\n\t\t}\n\t}\n");	
				}
			}
		}
		if (qc->qbehaviors != NULL &&
			qc->qbehaviors->length > 0){
			
			printf("\tThis class has %d quantum behaviors\n",qc->qbehaviors->length);
			//retrieves an array of quantum behaviors for this quantum class
			Node ** behaviors_array = qc->qbehaviors->getArray(qc->qbehaviors);
			
			//print all the quantum behaviors
			for(j=0;j< qc->qbehaviors->length; j++){
				QBehavior * qb = (QBehavior*)((Node*)(behaviors_array[j]))->value;
				printf("\tIteration [%d]\n",j);
				printf("\tbehaviors_array[%d]=[%s]\n",j,qb->name);
				printf("Access Modifier: [%s]\n",qb->access_modifier.line==-1?"":qb->access_modifier.name);
				if(qb->code_length > 0){
					int counter = 0;
					printf("\n\t\tCode length=[%d] {\n\t\t",qb->code_length);
					for(;counter < qb->code_length;++counter)
						printf("%c",qb->code[counter]);
					printf("\n\t\t}\n\t}\n");	
				}
				
			}
		}
		
		printf("\n}");
		
		
	}

}

{SPACE} { 
	printf(" [SPACE] ");
	yytext[0]=' ';
	goto RESTO;
}

{OPEN_SCOPE} { 
	printf("[OPEN_SCOPE]\n"); 
	
	opened_scopes++; 
	
	printf("[OPEN_SCOPE - MODE IS %d]\n",mode);
	printf("[OPEN_SCOPE - OPENED_SCOPES IS %d]\n",opened_scopes);
	
	if(mode==0 || (mode==1 && mode < opened_scopes))
		fprintf(file,"{");
	else{
		//yytext[0]='{';
		//goto RESTO;
	}
	
}

{CLOSE_SCOPE} { 
	
	if (opened_scopes == 0){
		printf("SYNTAX ERROR AT LINE %d\n",lines);
	}else{
		printf("[CLOSE_SCOPE]\n"); opened_scopes--;
		//check
		printf("BEFORE - MODE: [%d]\tOPENED_SCOPES: [%d]\n",mode,opened_scopes);
		if(mode==0 || (mode==opened_scopes)){
			printf("\nMODE IS 0 AND I WILL PRINT A }\n");
			fprintf(file,"}");
		}
		//else{
		//	yytext[0]='}';
		//	goto RESTO;
		//}
		mode = mode >> 1;
		/*if( !(mode == 1 && opened_scopes >= 1))
			if(mode==3) mode = 1;
			else mode--;*/
		
		//if(mode<0) mode=0;
		
		printf("AFTER - MODE: [%d]\tOPENED_SCOPES: [%d]\n",mode,opened_scopes);
	}

 }

{ACCESS_MODIFIER} { 
	printf("[ACCESS_MODIFIER=%s] ",yytext); 
	strcpy(lastAccessModifier.name,yytext);
	lastAccessModifier.line = lines;
	if(mode==0)
		fprintf(file,"%s",yytext);
}	

{NEW_LINE} { 
	printf("[NEW_LINE]\n"); lines++; 
	//if(mode==0)
		//fprintf(file,"\n");
}	



{QCLASS} { 
	printf("[QCLASS=%s] ",yytext);
	mode = 1;
	char delims[] = " ";
	char *result = NULL;
	result = strtok( yytext, delims );

	while( result != NULL ) {
		int len = strlen(result);
		if (result[len-1] == '{')
			result[len-1] = '\0';
		
		if(strcmp(result,"class")==0){
			char *class_name;
			
			result = strtok( NULL, delims );
			class_name = (char *) malloc(sizeof(char)*(strlen(result)+1));
			//result = strtok( NULL, delims );
			strcpy(class_name,result);
			Node * no;
			
			QClass * qc = InitializeQClass(class_name);
			free(class_name);
			
			if(lines == lastAccessModifier.line)
				qc->access_modifier = lastAccessModifier;

			strcpy(qc -> name, result);
			
			
			qc->qstates = InitializeLinkedList();
			qc->qbehaviors = InitializeLinkedList();
			
			no = createNode(qc); 
			addNode(no,qclasses_ll);
			break;
		}
		result = strtok( NULL, delims );

	}
	contador++;
}

{QSTATE} {
	printf("[QSTATE=%s]",yytext);
	mode = 2;
	char delims[] = " ";
	char *result = NULL;
	
	if(opened_scopes <= 0){
		printf("\nSYNTAX ERROR! A QUANTUM STATE SHOULD BE DECLARED INSIDE A QUANTUM CLASS!\n");
		yyterminate();
	}else if (opened_scopes > 1){
		printf("\nSYNTAX ERROR! Are you trying to declare a quantum state inside an invalid scope?\n");
		yyterminate();
	}
		
	
	result = strtok( yytext, delims );
	while( result != NULL ) {
		//printf("RESULT=[%s]\n",result);
		int len = strlen(result);
		if (result[len-1] == '{')
			result[len-1] = '\0';
		
		if(strcmp(result,"state")==0){
			char *state_name;
			
			result = strtok( NULL, delims );
			
			state_name = (char *) malloc(sizeof(char)*(strlen(result)+1));
			strcpy(state_name,result);
			
			QState * qs = InitializeQState(state_name);
			free(state_name);
			
			if(lines == lastAccessModifier.line)
				qs->access_modifier = lastAccessModifier;
			
			
			QClass *qc = (QClass *) getNode(qclasses_ll -> length -1, qclasses_ll)->value;			
			qs->qclass = qc;
			
			Node * no = createNode(qs); 
			
			#ifdef DEBUG
				if(qc->qstates==NULL) printf("Oh no! qc->qstates is NULL!");
				printf("\nQC->NAME IS [%s]\n",qc->name);
				printf("How many states? A: [%d]\n",qc->qstates->length);
			#endif
			
			addNode(no,qc->qstates);
			break;
		}
		result = strtok( NULL, delims );
		
	}
}


{QBEHAVIOR} {
	printf("[QBEHAVIOR=%s]",yytext);
	mode = 3;
	char delims[] = " ";
	char *result = NULL;
	
	if(opened_scopes <= 0){
		printf("\nSYNTAX ERROR! A QUANTUM BEHAVIOR SHOULD BE DECLARED INSIDE A QUANTUM CLASS!\n");
		yyterminate();
	}else if (opened_scopes > 1){
		printf("\nSYNTAX ERROR! Are you trying to declare a quantum behavior inside an invalid scope?\n");
		yyterminate();
	}
		
	
	result = strtok( yytext, delims );
	while( result != NULL ) {
		//printf("RESULT=[%s]\n",result);
		int len = strlen(result);
		if (result[len-1] == '{')
			result[len-1] = '\0';
		
		if(strcmp(result,"behavior")==0){
			char *behavior_name;
			
			result = strtok( NULL, delims );
			
			behavior_name = (char *) malloc(sizeof(char)*(strlen(result)+1));
			strcpy(behavior_name,result);
			
			QBehavior * qb = InitializeQBehavior(behavior_name);
			free(behavior_name);
			
			if(lines == lastAccessModifier.line)
				qb->access_modifier = lastAccessModifier;
			
			QClass *qc = (QClass *) getNode(qclasses_ll -> length -1, qclasses_ll)->value;			
			qb->qclass = qc;
			
			Node * no = createNode(qb); 
			
			#ifdef DEBUG
				if(qc->qbehaviors==NULL) printf("Oh no! qc->behaviors is NULL!");
				printf("\nQC->NAME IS [%s]\n",qc->name);
				printf("How many behaviors? A: [%d]\n",qc->qbehaviors->length);
			#endif
			
			addNode(no,qc->qbehaviors);
			break;
		}
		result = strtok( NULL, delims );
		
	}

}

{QENTANGLES} {
	printf("[QENTANGLES] [%s]",yytext); 
	char delims[] = " ";
	char *result = NULL;
	result = strtok( yytext, delims );
	result = strtok( NULL, delims );
	
	if(mode != 2)
	{
		printf("\nSYNTAX ERROR! Quantum entanglement is only allowed to quantum states. Did you miss your quantum physics classes? :)\n");
		yyterminate();
	}
	QClass *qc = (QClass *) getNode(qclasses_ll -> length -1, qclasses_ll)->value;
	QState *qs = (QState*)((QClass *) getNode(qclasses_ll -> length -1, qclasses_ll)->value)->qstates->tail->value;
	
	//TODO: check if the next line could cause any problem in the future
	QBehavior * qb = InitializeQBehavior(result);
	Node * no = createNode(qb); 
	addNode(no,qs->qbehaviors);
	
	
	
	
	}

{END} { yyterminate(); }
{QINVOKEBEHAVIOR} {
	printf("[QINVOKEBEHAVIOR]=[%s]\n",yytext);
	
	char * variable, * qbehavior_name, *yytext_bkp;
	
	yytext_bkp = (char*)malloc(sizeof(char)*(strlen(yytext)+1));
	strcpy(yytext_bkp,yytext);
	
	char delims[] = " .(";
	char *result = NULL;
	result = strtok( yytext_bkp, delims );
	printf("[1st RESULT]=[%s]\n",result);
	if(result == NULL || strlen(result)==0){
		printf("\nSYNTAX ERROR: [%s]\n",yytext);
		yyterminate();
	}
	variable = (char*)malloc(sizeof(char)*(strlen(result)+1));
	strcpy(variable,result);
	result = strtok( NULL, delims );
	printf("[2nd RESULT]=[%s]\n",result);
	if(result == NULL || strlen(result)==0){
		printf("\nSYNTAX ERROR: [%s]\n",yytext);
		yyterminate();
	}
	qbehavior_name = (char*)malloc(sizeof(char)*(strlen(result)+1));
	strcpy(qbehavior_name,result);
	
	QBehavior *qb = getQBehavior2(qclasses_ll, qbehavior_name);
	if(qb==NULL){
		#ifdef DEBUG
			printf("No quantum behavior named [%s] was found!\n",qbehavior_name);
		#endif
		fprintf(file,"%s",yytext);
	}else{
		printf("A quantum behavior named [%s] was FOUND! :-)\n",qbehavior_name);
		fprintf(file,"%s.qBehavior(%sBehaviors.%s);\n",variable,qb->qclass->name,toUpperCase(qbehavior_name));
	}

}
{QSCOLLAPSE} {
	char * variable, * qstate_name, *yytext_bkp;
	
	yytext_bkp = (char*)malloc(sizeof(char)*(strlen(yytext)+1));
	strcpy(yytext_bkp,yytext);
	
	printf("[QSCOLLAPSE]=[%s]\n",yytext_bkp);
	char delims[] = " ->;";
	char *result = NULL;
	result = strtok( yytext_bkp, delims );
	printf("[1st RESULT]=[%s]\n",result);
	if(result == NULL || strlen(result)==0){
		printf("\nSYNTAX ERROR: [%s]\n",yytext);
		yyterminate();
	}
	variable = (char*)malloc(sizeof(char)*(strlen(result)+1));
	strcpy(variable,result);
	result = strtok( NULL, delims );
	printf("[2nd RESULT]=[%s]\n",result);
	if(result == NULL || strlen(result)==0){
		printf("\nSYNTAX ERROR: [%s]\n",yytext);
		yyterminate();
	}
	qstate_name = (char*)malloc(sizeof(char)*(strlen(result)+1));
	strcpy(qstate_name,result);
	//
	QState * qs = getQState (qclasses_ll, qstate_name);
	if(qs==NULL){
		printf("No quantum state named [%s] was found!\n",qstate_name);
		fprintf(file,"throw new NotCollapsedException(\"%s\");\n",yytext);
	}else{
		printf("A quantum state named [%s] was FOUND! :-)\n",qstate_name);
		fprintf(file,"%s.set%sDataStructure(new %sDataStructure(%sDSTypes.%s));\n",variable,qs->qclass->name,qs->qclass->name,qs->qclass->name,toUpperCase(qstate_name));
	}
 }

{RESTO} { 

RESTO:

	switch(mode){
		case 0: 
			printf("[IN]%s[/IN]",yytext); 
			//char * yytext_bkp = (char *)malloc(sizeof(char) * strlen(yytext));
			//strcpy(yytext_bkp,yytext);
			//OK, first of all, we to check if the first string is a quantum class name.
			//if so, the users is declaring a new quantum object, and we need to register it.
			//char delims[] = " \t";
			//char *result = NULL;
			//result = strtok( yytext_bkp, delims );
			//printf("[1st STRING]=[%s]\n",result);
			
			//QClass * qct = getQClass(qclasses_ll,result);
			
			//if(qct==NULL){
			//	printf("\nNo quantum class named [%s] was found!\n",result);
				
			//}else{
			//	printf("\nQuantum class named [%s] FOUND!\n",result);
			//}
			
			fprintf(file,"%s",yytext);
		break;
		
		case 1:
			#ifdef DEBUG
				printf("Add %c to quantum class\n",yytext[0]);
			#endif
			printf("QCLASS=[%s]\n",yytext);
			QClass *qc = (QClass *) getNode(qclasses_ll -> length -1, qclasses_ll)->value;
			if(qc->code_length==0)
				qc->code = (char *) malloc(sizeof(char)*500);
			else if( (qc->code_length + 1) % 500 == 0){
					qc->code = (char *)realloc(qc->code, qc->code_length + 500);
			}
			qc->code[qc->code_length++]=yytext[0];
		break;
		
		case 2: 
			#ifdef DEBUG
				printf("Add %c to quantum state\n",yytext[0]);
			#endif
			printf("QSTATE=[%s]",yytext);
			QState *qs = (QState*)((QClass *) getNode(qclasses_ll -> length -1, qclasses_ll)->value)->qstates->tail->value;
			
			if(qs->code_length==0)
				qs->code = (char *) malloc(sizeof(char)*500);
			else if( (qs->code_length + 1) % 500 == 0){
					qs->code = (char *)realloc(qs->code, qs->code_length + 500);
			}
			qs->code[qs->code_length++]=yytext[0];

		
		
		break;
		
		case 3: 
			printf("QBEHAVIOR=[%s]",yytext);
			#ifdef DEBUG
				printf("Add %c to quantum behavior\n",yytext[0]);
			#endif
			QBehavior *qb = (QBehavior*)((QClass *) getNode(qclasses_ll -> length -1, qclasses_ll)->value)->qbehaviors->tail->value;
			
			if(qb->code_length==0)
				qb->code = (char *) malloc(sizeof(char)*500);
			else if( (qb->code_length + 1) % 500 == 0){
					qb->code = (char *)realloc(qb->code, qb->code_length + 500);
			}
			qb->code[qb->code_length++]=yytext[0];
		
		
		
		
		
		
		break;
	
	
	}

}


%%

main( argc, argv )
int argc;
char **argv;
{
	int i;
	//ignoring the program name
	++argv, --argc;
	//printf("%s",argv[0]);
	//printf("%d", argc ); 

	qclasses_ll = InitializeLinkedList();
	
	
	if ( argc > 0 )
	{
	yyin = fopen( argv[0], "rb" );
	}
	else
	{
	yyin = stdin;
	}

	file = fopen("output.java","w+");
	
	fprintf(file,"class NotCollapsedException extends Exception{public NotCollapsedException (String s){System.out.println(\"Exception: \" + s);}}\n");
	
	printf("*********** START ***********\n\n");
	yylex();
	
	printf("Starting the generation of Java source code...");
	
	
	
	
	createStates(qclasses_ll,file);
	createBehaviors(qclasses_ll,file);
	createClasses(qclasses_ll,file);

	qclasses_ll -> clear(qclasses_ll);


}

